package com.cby.annotation.process;

import com.cby.android.annotation.Provider;
import com.cby.android.annotation.RouteAnnotation;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;

/**
 * @description: AnnotationProcessor
 * @author: ChenBaoYang
 * @createDate: 2022/3/22 2:08 下午
 */
public class AnnotationProcessor extends AbstractProcessor {

    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        filer = processingEnv.getFiler();
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        LinkedHashSet<String> set = new LinkedHashSet<>();
        set.add(RouteAnnotation.class.getCanonicalName());
        return set;
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        System.out.println("process");
        HashMap<String, String> nameMap = new HashMap<>();

        Set<? extends Element> annotationElements = roundEnv.getElementsAnnotatedWith(RouteAnnotation.class);

        for (Element element : annotationElements) {
            RouteAnnotation annotation = element.getAnnotation(RouteAnnotation.class);
            String name = annotation.name();
            String clzName = element.getSimpleName().toString();
            nameMap.put(name, clzName);
            System.out.println("name = "+name+",clzName = "+clzName);
        }
        //生成 Java 文件
        generateJava(nameMap);

        return true;
    }

    private void generateJava(Map<String, String> nameMap) {

        // 生成构造方法
        MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .addStatement("routeMap = new $T<>()", HashMap.class);

        for (String name: nameMap.keySet()) {
            // 获取注解的activity的名称
            String activityName = nameMap.get(name);
            // 将注解的name和activity的名称存入到routeMap 中
            constructorBuilder.addStatement("routeMap.put($S, $S)", name, activityName);

            System.out.println("name = "+name+",activityName = "+activityName);
        }
        // 获取构造函数的MethodSpec对象
        MethodSpec constructorName = constructorBuilder.build();

        // 生成 getActivityRouteName 方法
        MethodSpec getActivityName = MethodSpec.methodBuilder("getActivityRouteName")
                // public的方法
                .addModifiers(Modifier.PUBLIC)
                // 方法返回类型为字符串
                .returns(String.class)
                // 添加方法参数
                .addParameter(String.class, "routeName")
                // 添加判断语句代码块
                .beginControlFlow("if(routeMap != null && !routeMap.isEmpty())")
                // 添加条件语句内容代码块
                .addStatement("return (String)routeMap.get(routeName)")
                // 条件判断
                .endControlFlow()
                // 添加条件语句外的返回
                .addStatement("return \"\"")
                .build();

        // 生成类
        TypeSpec typeSpec = TypeSpec.classBuilder("RouteAnnotation$Finder")
                .addModifiers(Modifier.PUBLIC)
                .addMethod(constructorName)
                .addMethod(getActivityName)
                .addSuperinterface(Provider.class)
                // 添加成员变量
                .addField(HashMap.class,"routeMap", Modifier.PRIVATE)
                .build();

        // 生成java文件
        JavaFile javaFile = JavaFile.builder("com.cby.annotation.process",typeSpec).build();
        try {
            javaFile.writeTo(filer);
        } catch (IOException e) {
            System.out.println("e = "+e.getMessage());
            e.printStackTrace();
        }
    }
}
